package com.scs.application.modules.upms.service.impl;

import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.Lists;
import com.scs.application.core.excel.handler.AggregationDataHandler;
import com.scs.application.core.exceptions.BusinessException;
import com.scs.application.core.global.GlobalParamService;
import com.scs.application.core.password.PasswordChecker;
import com.scs.application.core.service.BaseServiceImpl;
import com.scs.application.core.utils.*;
import com.scs.application.modules.base.excel.handler.ExcelImportUtilCustom;
import com.scs.application.modules.upms.dto.UserDTO;
import com.scs.application.modules.upms.entity.Role;
import com.scs.application.modules.upms.entity.User;
import com.scs.application.modules.upms.entity.WxmpMember;
import com.scs.application.modules.upms.excel.handler.OfficeDataHandler;
import com.scs.application.modules.upms.excel.handler.UserDataHandler;
import com.scs.application.modules.upms.mapper.RoleMapper;
import com.scs.application.modules.upms.mapper.UserMapper;
import com.scs.application.modules.upms.request.UserQueryRequest;
import com.scs.application.modules.upms.security.UserPrincipal;
import com.scs.application.modules.upms.service.UserService;
import com.scs.application.modules.upms.service.WxmpMemberService;
import com.scs.application.modules.upms.vo.UserVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;

/**
 **/
@Slf4j
@AllArgsConstructor
@Service
public class UserServiceImpl extends BaseServiceImpl<UserMapper, User> implements UserService {

    private final WxmpMemberService wxmpMemberService;

    private final RoleMapper roleMapper;

    @Override
    public List<UserVO> list(UserQueryRequest request) {
        UserPrincipal currentUser = UserUtils.currentUser();
        if (!currentUser.isAdmin()
                && StringUtils.isNotBlank(currentUser.getOfficeId())
                && StringUtils.isBlank(request.getOfficeId())) {
            request.setOfficeId(currentUser.getOfficeId());
        }
        return this.baseMapper.list(request);
    }

    @Override
    public IPage<UserVO> page(UserQueryRequest request) {
        UserPrincipal currentUser = UserUtils.currentUser();
        if (!currentUser.isAdmin() && StringUtils.isBlank(request.getOfficeId())) {
            request.setOfficeId(currentUser.getOfficeId());
        }
        return this.baseMapper.page(request.getPage(), request);
    }


    /**
     * checkUserMobile 避免重复mobile
     *
     * @param mobile
     * @return
     */
    @Override
    public boolean checkUserMobile(String mobile, String userId) {
        if (StringUtils.isBlank(mobile)) {
            return false;
        }
        int count = this.count(Wrappers.<User>query().eq("mobile", mobile).ne("id", userId));
        return count > 0;
    }

    /**
     * checkUserEmail 避免重复email
     *
     * @param email
     * @return
     */
    @Override
    public boolean checkUserEmail(String email, String userId) {
        if (StringUtils.isBlank(email)) {
            return false;
        }
        int count = this.count(Wrappers.<User>query().eq("email", email).ne("id", userId));
        return count > 0;
    }


    @Override
    public boolean existLoginKey(String loginKey) {
        int count = this.count(Wrappers.<User>query().eq("login_key", loginKey)
                .or().eq("login_key", Strings.toUpperCase(loginKey))
                .or().eq("login_key", Strings.toLowerCase(loginKey)));
        return count > 0;
    }

    @Override
    public User findByLoginKey(String loginKey) {
        return this.getOne(Wrappers.<User>query().eq("login_key", loginKey)
                .or().eq("login_key", Strings.toUpperCase(loginKey))
                .or().eq("login_key", Strings.toLowerCase(loginKey)));
    }

    /**
     * 获取用户信息
     *
     * @param userKey
     * @return
     */
    @Override
    public UserVO findInfoByLoginKey(String userKey) {
        User user = this.findByLoginKey(userKey);
        if (user == null) {
            return null;
        }

        UserVO vo = new UserVO();
        org.springframework.beans.BeanUtils.copyProperties(user, vo);

        setAuthorize(vo);
        return vo;
    }

    @Override
    public List<User> findAllByLoginKey(String loginKey) {
        UserQueryRequest queryRequest = new UserQueryRequest();
        queryRequest.setLoginKey(loginKey);
        return baseMapper.selectByLoginKey(queryRequest);
    }

    @Override
    public User findByWxmpOpenId(String openId) {
        // 如果存在多个，默认取第一个
        WxmpMember member = wxmpMemberService.getOne(Wrappers.<WxmpMember>query().eq("open_id", openId)
                .orderByAsc("gmt_joined")
                .last("limit 1"));
        if (member != null) {
            return this.getById(member.getUserId());
        }

        return this.getById(member.getUserId());
    }

    @Override
    public UserVO findInfoByWxmpOpenId(String openId) {
        User user = this.findByWxmpOpenId(openId);
        if (user == null) {
            return null;
        }

        UserVO vo = new UserVO();
        org.springframework.beans.BeanUtils.copyProperties(user, vo);

        setAuthorize(vo);
        return vo;
    }

    @Override
    public User findByMobile(String mobile) {
        return this.getOne(Wrappers.<User>query().eq("mobile", mobile));
    }

    @Override
    public List<User> findAllByMobile(String mobile) {
        return baseMapper.selectList(Wrappers.<User>query().eq("mobile", mobile));
    }

    @Override
    public UserVO findInfoByMobile(String mobile) {
        User user = this.findByMobile(mobile);
        if (user == null) {
            return null;
        }

        UserVO vo = new UserVO();
        org.springframework.beans.BeanUtils.copyProperties(user, vo);

        setAuthorize(vo);
        return vo;
    }

    @Override
    public User findByEmail(String email) {
        return this.getOne(Wrappers.<User>query().eq("email", email));
    }

    @Override
    public UserVO findInfoByEmail(String email) {
        User user = this.findByEmail(email);
        if (user == null) {
            return null;
        }

        UserVO vo = new UserVO();
        org.springframework.beans.BeanUtils.copyProperties(user, vo);

        setAuthorize(vo);
        return vo;
    }

    /*
     * 级联保存。保存时检查用户是否已存在，设置默认密码
     * @param entity
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveCascadeById(User entity) {

        if (this.existLoginKey(entity.getLoginKey())) {
            throw new BusinessException("登录账号已存在");
        } else if (this.checkUserMobile(entity.getMobile(), entity.getId())) {
            throw new BusinessException("手机号已被注册");
        } else if (this.checkUserEmail(entity.getEmail(), entity.getId())) {
            throw new BusinessException("邮箱已被注册");
        }


        entity.setInactive(User.INACTIVE_ENABLED);
        entity.setPassword(Md5Utils.md5(StringUtils.defaultString(entity.getPassword(), getDefaultPassword())));

        return super.saveCascadeById(entity);
    }

    /*
     * 级联保存。保存时检查用户是否已存在，判断手机号和邮箱
     * @param entity
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateCascadeById(User entity) {
        if (this.checkUserMobile(entity.getMobile(), entity.getId())) {
            throw new BusinessException("手机号已被注册");
        } else if (this.checkUserEmail(entity.getEmail(), entity.getId())) {
            throw new BusinessException("邮箱已被注册");
        }
        return super.updateCascadeById(entity);
    }

    /**
     * 修改用户密码
     *
     * @param userId
     * @param oldPassword
     * @param newPassword
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean modifyPassword(String userId, String oldPassword, String newPassword) {
        pwdStrengthCheck(newPassword);

        User user = this.getById(userId);
        if (user == null) {
            throw new BusinessException("用户不存在");
        }

        if (!oldPassword.equals(Md5Utils.md5(user.getPassword()))
                && !oldPassword.equals(user.getPassword())
                && !Md5Utils.md5(oldPassword).equals(user.getPassword())

        ) {
            throw new BusinessException("旧密码输入错误");
        }
        User tmp = new User();
        tmp.setId(user.getId());
        tmp.setPassword(Md5Utils.md5(newPassword));


        return super.updateById(tmp);

    }

    @Override
    public void resetPassword(String userId) {
        User tmp = new User();
        tmp.setId(userId);
        tmp.setPassword(getDefaultPassword());
        super.updateById(tmp);
    }

    @Override
    public boolean unassignRole(String userId, String roleId) {
        User user = getById(userId);
        if (user == null) {
            return false;
        }
        JSONArray rolesId = user.getRolesId();
        if (roleId == null) {
            return false;
        }
        rolesId.remove(roleId);

        User tmp = new User();
        tmp.setRolesId(rolesId).setId(userId);

        return this.updateById(tmp);
    }

    @Override
    public boolean assignRole(List<String> userIdList, String roleId) {

        // 检测当前用户能否分配指定的角色

        Collection<User> users = this.listByIds(userIdList);
        for (User user : users) {
            JSONArray rolesId = user.getRolesId();
            if (!rolesId.contains(roleId)) {
                rolesId.add(roleId);
            }
            User tmp = new User();
            tmp.setRolesId(rolesId).setId(user.getId());
            this.updateById(tmp);
        }
        return true;
    }

    @Override
    public List<Role> getUserRoles(String userId) {

        User user = getById(userId);
        if (user == null) {
            return Collections.emptyList();
        }
        List<Role> roles = roleMapper.selectBatchIds(user.getRolesId().toJavaList(String.class));
        return roles;
    }

    @Override
    public void changeRoles(String loginKey, List<String> toRoleCodes) {

        User user = findByLoginKey(loginKey);
        if (user == null) {
            return;
        }
        String userId = user.getId();

        List<Role> roles = roleMapper.selectList(Wrappers.<Role>query().in("code", toRoleCodes));
        user = new User();
        user.setId(userId);

        Set<String> roleIdSet = roles.stream().map(o -> o.getId()).collect(Collectors.toSet());
        user.setRolesId(JSONObject.parseArray(JSONObject.toJSONString(roleIdSet)));
        this.updateById(user);
    }

    @Override
    public void changeLoginKey(String oldLoginKey, String newLoginKey) {
        boolean existLoginKey = existLoginKey(newLoginKey);
        if (existLoginKey) {
            throw new BusinessException(String.format("登录账号%s已存在", newLoginKey));
        }
        update(new UpdateWrapper<User>().set("login_key", newLoginKey)
                .eq("login_key", oldLoginKey));
    }


    @Override
    protected ExcelImportResult doExcelImport(MultipartFile multipartFile, Map<String, Object> params) throws Exception {
        ImportParams importParams = PoiUtils.getDefaultImportParams();

        importParams.setDataHandler(new AggregationDataHandler(Lists.newArrayList(new OfficeDataHandler(), new UserDataHandler())));
        ExcelImportResult importResult = ExcelImportUtilCustom.importExcelMore(multipartFile.getInputStream(), UserDTO.class, importParams);
        List<UserDTO> list = importResult.getList();

        List<User> entityList = Lists.newArrayListWithExpectedSize(list.size());
        BeanUtils.copyProperties(list, entityList, User.class);

        for (User user : entityList) {
            if (StringUtils.isBlank(user.getId())) {
                user.setPassword(Md5Utils.md5(getDefaultPassword()));
            }
        }
        if (!entityList.isEmpty()) {
            this.saveOrUpdateBatch(entityList);
        }
        return importResult;
    }

    /**
     * 密码强度校验
     *
     * @param password
     */
    private void pwdStrengthCheck(String password) {
        PasswordChecker passwordChecker = new PasswordChecker();
        passwordChecker.setLowerCase(true);
        passwordChecker.setUpperCase(true);
        passwordChecker.setSpecial(false);

        // 去除密码
        /*if (!passwordChecker.check(password)) {
            throw new BusinessException("您的密码强度太弱，密码需要包含大小写字母、数字，长度为8-20位之间");
        }*/
    }

    private void setAuthorize(UserVO vo) {
        List<String> permissions = Lists.newArrayList();
        List<String> roles = Lists.newArrayList();

        if ("admin".equals(vo.getLoginKey())) {
            permissions.add("*");
            roles.add("*");
        } else {
            permissions.add("test:*");
            roles.add("test");
        }
        vo.setRoleCodes(roles);
        vo.setPermissions(permissions);
    }

    private String getDefaultPassword() {
        return GlobalParamService.getInstance().getDefaultPassword();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public User addUser(UserVO uservo) {
        List<UserVO> userVOs = new ArrayList<>();
        userVOs.add(uservo);
        List<User> users = batchAddUser(userVOs);
        return users != null && users.size() > 0 ? users.get(0) : null;
    }

    public List<User> batchAddUser(List<UserVO> users) {
        for (UserVO vo : users) {
            if (this.existLoginKey(vo.getLoginKey())) {
                throw new BusinessException("登录账号" + vo.getLoginKey() + "已存在");
            }
        }
        List<User> insertedUser = Lists.newArrayList();
        for (UserVO vo : users) {
            User user = new User();
            BeanUtils.copyProperties(vo, user);
            user.setInactive(User.INACTIVE_ENABLED)
                    .setPassword(Md5Utils.md5(getDefaultPassword()));

            JSONArray roleIds = vo.getRolesId();
            if (roleIds != null && roleIds.size() > 0) {
                user.setRolesId(roleIds);
            }

            List<String> roleCodes = vo.getRoleCodes();
            if (roleCodes != null && roleCodes.size() > 0) {
                List<String> roleIdList = roleMapper.selectObjs(Wrappers.<Role>query().select("id").in("code", roleCodes))
                        .stream().filter(Objects::nonNull).map(o -> o.toString()).collect(Collectors.toList());
                JSONArray jsonArray = new JSONArray();
                jsonArray.addAll(roleIdList);
                user.setRolesId(jsonArray);
            }
            insertedUser.add(user);
        }

        super.saveBatch(insertedUser);
        return insertedUser;
    }
}

